!-------------------------------------------------------------LICENSE--------------------------------------------------------------!
!                                                                                                                                  !
!The MAP code is written in Fortran language for magnetohydrodynamics (MHD) calculation with the adaptive mesh refinement (AMR)    !
!and Message Passing Interface (MPI) parallelization.                                                                              !
!                                                                                                                                  !
!Copyright (C) 2012                                                                                                                !
!Ronglin Jiang                                                                                                                     !
!rljiang@ssc.net.cn                                                                                                                !
!585 Guoshoujing Road. Pudong, Shanghai, P.R.C. 201203                                                                             !
!                                                                                                                                  !
!This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License         !
!as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.             !
!                                                                                                                                  !
!This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of    !
!MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.                        !
!                                                                                                                                  !
!You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software     !
!Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.                                                   !
!                                                                                                                                  !
!-------------------------------------------------------------LICENSE--------------------------------------------------------------!

!==================================================================================================================================|
subroutine load_state(base)
!==================================================================================================================================|

   use parameters
   use block_define
   use interface_block_generate
   use interface_hilbert_curve
   implicit none

   type(block), pointer, intent(in) :: base

   character(4) :: n_save_state_char, cpu_char
   integer(4) :: n_cpu, i_level, i_g, pos_x, pos_y, pos_z, hilbert_number, id(n_dims, n_levels), n_gene_input
   integer(4), allocatable :: gene_input(:, :, :)
   logical(4) :: flag
   real(8) :: x(nx_block), y(ny_block), z(nz_block)

   type(block), pointer :: current, framework_next, pointer_tmp, last, next

!----------------------------------------------------------------------------------------------------------------------------------|
   write(n_save_state_char,'(i4.4)') n_load_state
   write(cpu_char,'(i4.4)') cpu_id

   open (unit_save_state, file = trim(datamap_path) // 'state.n' // n_save_state_char // '.cpu' // cpu_char // '.mapout',          &
      form = 'unformatted', status = 'old', iostat = error)

   if (error .ne. 0) then
      write (*, '(a)') ' The saved state file does not exist.'
      call mpi_abort(mpi_comm_world, 1, error)
   endif

   read(unit_save_state) n_cpu

   if (cpu_number .ne. n_cpu) then
      write (*, '(a)') ' The number of CPUs is not correct.'
      call mpi_abort(mpi_comm_world, 1, error)
   endif

   read(unit_save_state) max_speed
   read(unit_save_state) max_et
   read(unit_save_state) max_te_ro
   read(unit_save_state) min_dt
   read(unit_save_state) min_dt_hc
   read(unit_save_state) max_level
   read(unit_save_state) ns
   read(unit_save_state) nt
   read(unit_save_state) t
   read(unit_save_state) t_regrid
   read(unit_save_state) base%dt
   read(unit_save_state) base%t
   read(unit_save_state) n_save_state
   read(unit_save_state) n_blocks
   read(unit_save_state) n_blocks_all
   read(unit_save_state) n_gene_input
   allocate(gene_input(n_dims, n_levels, n_gene_input))
   read(unit_save_state) gene_input
   read(unit_save_state) hilbert_start
   read(unit_save_state) hilbert_end
   read(unit_save_state) reflux_size_x
   read(unit_save_state) reflux_size_y
   read(unit_save_state) reflux_size_z
   read(unit_save_state) shareblock_size_x
   read(unit_save_state) shareblock_size_y
   read(unit_save_state) shareblock_size_z
   read(unit_save_state) exchange_size_x
   read(unit_save_state) exchange_size_y
   read(unit_save_state) exchange_size_z
   read(unit_save_state) reflux_size_send_x
   read(unit_save_state) reflux_size_recv_x
   read(unit_save_state) reflux_size_send_y
   read(unit_save_state) reflux_size_recv_y
   read(unit_save_state) reflux_size_send_z
   read(unit_save_state) reflux_size_recv_z
   read(unit_save_state) shareblock_size_send_x
   read(unit_save_state) shareblock_size_recv_x
   read(unit_save_state) shareblock_size_send_y
   read(unit_save_state) shareblock_size_recv_y
   read(unit_save_state) shareblock_size_send_z
   read(unit_save_state) shareblock_size_recv_z
   read(unit_save_state) exchange_size_send_x
   read(unit_save_state) exchange_size_recv_x
   read(unit_save_state) exchange_size_send_y
   read(unit_save_state) exchange_size_recv_y
   read(unit_save_state) exchange_size_send_z
   read(unit_save_state) exchange_size_recv_z

   deallocate(neighs_reflux_info_send_x,                                                                                           &
              neighs_reflux_info_send_y,                                                                                           &
              neighs_reflux_info_send_z,                                                                                           &
              neighs_reflux_info_recv_x,                                                                                           &
              neighs_reflux_info_recv_y,                                                                                           &
              neighs_reflux_info_recv_z,                                                                                           &
              neighs_shareblock_info_send_x,                                                                                       &
              neighs_shareblock_info_send_y,                                                                                       &
              neighs_shareblock_info_send_z,                                                                                       &
              neighs_shareblock_info_recv_x,                                                                                       &
              neighs_shareblock_info_recv_y,                                                                                       &
              neighs_shareblock_info_recv_z,                                                                                       &
              neighs_exchange_info_send_x,                                                                                         &
              neighs_exchange_info_send_y,                                                                                         &
              neighs_exchange_info_send_z,                                                                                         &
              neighs_exchange_info_recv_x,                                                                                         &
              neighs_exchange_info_recv_y,                                                                                         &
              neighs_exchange_info_recv_z,                                                                                         &
              neighs_reflux_send_x,                                                                                                &
              neighs_reflux_send_y,                                                                                                &
              neighs_reflux_send_z,                                                                                                &
              neighs_reflux_recv_x,                                                                                                &
              neighs_reflux_recv_y,                                                                                                &
              neighs_reflux_recv_z,                                                                                                &
              neighs_shareblock_send_x,                                                                                            &
              neighs_shareblock_send_y,                                                                                            &
              neighs_shareblock_send_z,                                                                                            &
              neighs_shareblock_recv_x,                                                                                            &
              neighs_shareblock_recv_y,                                                                                            &
              neighs_shareblock_recv_z,                                                                                            &
              neighs_exchange_send_x,                                                                                              &
              neighs_exchange_send_y,                                                                                              &
              neighs_exchange_send_z,                                                                                              &
              neighs_exchange_recv_x,                                                                                              &
              neighs_exchange_recv_y,                                                                                              &
              neighs_exchange_recv_z)

   allocate(neighs_reflux_info_send_x(n_d_l + 1, reflux_size_x, cpu_number))
   allocate(neighs_reflux_info_send_y(n_d_l + 1, reflux_size_y, cpu_number))
   allocate(neighs_reflux_info_send_z(n_d_l + 1, reflux_size_z, cpu_number))
   allocate(neighs_reflux_info_recv_x(n_d_l + 1, reflux_size_x, cpu_number))
   allocate(neighs_reflux_info_recv_y(n_d_l + 1, reflux_size_y, cpu_number))
   allocate(neighs_reflux_info_recv_z(n_d_l + 1, reflux_size_z, cpu_number))

   allocate(neighs_shareblock_info_send_x(n_d_l + 1, shareblock_size_x, cpu_number))
   allocate(neighs_shareblock_info_send_y(n_d_l + 1, shareblock_size_y, cpu_number))
   allocate(neighs_shareblock_info_send_z(n_d_l + 1, shareblock_size_z, cpu_number))
   allocate(neighs_shareblock_info_recv_x(n_d_l + 1, shareblock_size_x, cpu_number))
   allocate(neighs_shareblock_info_recv_y(n_d_l + 1, shareblock_size_y, cpu_number))
   allocate(neighs_shareblock_info_recv_z(n_d_l + 1, shareblock_size_z, cpu_number))

   allocate(neighs_exchange_info_send_x(n_d_l + 1, exchange_size_x, cpu_number))
   allocate(neighs_exchange_info_send_y(n_d_l + 1, exchange_size_y, cpu_number))
   allocate(neighs_exchange_info_send_z(n_d_l + 1, exchange_size_z, cpu_number))
   allocate(neighs_exchange_info_recv_x(n_d_l + 1, exchange_size_x, cpu_number))
   allocate(neighs_exchange_info_recv_y(n_d_l + 1, exchange_size_y, cpu_number))
   allocate(neighs_exchange_info_recv_z(n_d_l + 1, exchange_size_z, cpu_number))

   allocate(neighs_reflux_send_x(sys, ny_no_bnd, nz_no_bnd, reflux_size_send_x))
   allocate(neighs_reflux_send_y(sys, nx_no_bnd, nz_no_bnd, reflux_size_send_y))
   allocate(neighs_reflux_send_z(sys, nx_no_bnd, ny_no_bnd, reflux_size_send_z))
   allocate(neighs_reflux_recv_x(sys, ny_no_bnd, nz_no_bnd, reflux_size_recv_x))
   allocate(neighs_reflux_recv_y(sys, nx_no_bnd, nz_no_bnd, reflux_size_recv_y))
   allocate(neighs_reflux_recv_z(sys, nx_no_bnd, ny_no_bnd, reflux_size_recv_z))

   allocate(neighs_shareblock_send_x(sysp1, ng + 1, ny_block, nz_block, shareblock_size_send_x))
   allocate(neighs_shareblock_send_y(sysp1, nx_block, ng + 1, nz_block, shareblock_size_send_y))
   allocate(neighs_shareblock_send_z(sysp1, nx_block, ny_block, ng + 1, shareblock_size_send_z))
   allocate(neighs_shareblock_recv_x(sysp1, ng + 1, ny_block, nz_block, shareblock_size_recv_x))
   allocate(neighs_shareblock_recv_y(sysp1, nx_block, ng + 1, nz_block, shareblock_size_recv_y))
   allocate(neighs_shareblock_recv_z(sysp1, nx_block, ny_block, ng + 1, shareblock_size_recv_z))

   allocate(neighs_exchange_send_x(sysp1, ng, ny_no_bnd, nz_no_bnd, exchange_size_send_x))
   allocate(neighs_exchange_send_y(sysp1, nx_block, ng, nz_no_bnd, exchange_size_send_y))
   allocate(neighs_exchange_send_z(sysp1, nx_block, ny_block, ng, exchange_size_send_z))
   allocate(neighs_exchange_recv_x(sysp1, ng, ny_no_bnd, nz_no_bnd, exchange_size_recv_x))
   allocate(neighs_exchange_recv_y(sysp1, nx_block, ng, nz_no_bnd, exchange_size_recv_y))
   allocate(neighs_exchange_recv_z(sysp1, nx_block, ny_block, ng, exchange_size_recv_z))

   read(unit_save_state) n_neighs_reflux_sendrecv_x
   read(unit_save_state) n_neighs_reflux_sendrecv_y
   read(unit_save_state) n_neighs_reflux_sendrecv_z
   read(unit_save_state) n_neighs_shareblock_sendrecv_x
   read(unit_save_state) n_neighs_shareblock_sendrecv_y
   read(unit_save_state) n_neighs_shareblock_sendrecv_z
   read(unit_save_state) n_neighs_exchange_sendrecv_x
   read(unit_save_state) n_neighs_exchange_sendrecv_y
   read(unit_save_state) n_neighs_exchange_sendrecv_z
   read(unit_save_state) neighs_reflux_info_send_x
   read(unit_save_state) neighs_reflux_info_send_y
   read(unit_save_state) neighs_reflux_info_send_z
   read(unit_save_state) neighs_shareblock_info_send_x
   read(unit_save_state) neighs_shareblock_info_send_y
   read(unit_save_state) neighs_shareblock_info_send_z
   read(unit_save_state) neighs_exchange_info_send_x
   read(unit_save_state) neighs_exchange_info_send_y
   read(unit_save_state) neighs_exchange_info_send_z

!----------------------------------------------------------------------------------------------------------------------------------|
!  generate child block
!----------------------------------------------------------------------------------------------------------------------------------|
   do i_g = 1, n_gene_input
      i_level = 1
      current => base
      do while (gene_input(1, i_level, i_g) .ne. 0)
         pos_x = gene_input(1, i_level, i_g)
         pos_y = gene_input(2, i_level, i_g)
         pos_z = gene_input(3, i_level, i_g)
         if (current%nc .eq. 0) then
            call block_generate(current, current%cpu_id)
            next => current%next
            framework_next => current%framework_next
            pointer_tmp => current
            call hilbert_curve(current, pointer_tmp, current%parent_cell, current%hilbert_pos, 1, (/1, 1, 1/), current%cpu_id)
            if (current%cpu_id .eq. cpu_id) then
               next%last => pointer_tmp
               pointer_tmp%next => next
            endif
            framework_next%framework_last => pointer_tmp
            pointer_tmp%framework_next => framework_next
         endif
         current => current%child(pos_x, pos_y, pos_z)%pblock
         i_level = i_level + 1
         if (i_level .gt. n_levels) exit
      enddo
   enddo

!----------------------------------------------------------------------------------------------------------------------------------|
!  generate hilbert number
!----------------------------------------------------------------------------------------------------------------------------------|
   hilbert_number = 1
   current => base%framework_next
   do while (current%lv .ne. 0)
      current%hilbert_number = hilbert_number
      current => current%framework_next
      hilbert_number = hilbert_number + 1
   enddo

!if (cpu_id .eq. 0) then
!   current => base%framework_next
!   do while (current%lv .ne. 0)
!      print*, current%hilbert_number
!      current => current%framework_next
!   enddo
!pause
!endif

!----------------------------------------------------------------------------------------------------------------------------------|
!  generate links
!----------------------------------------------------------------------------------------------------------------------------------|
   last => base
   current => base%framework_next
   do while (current%lv .ne. 0)
      if (current%hilbert_number .ge. hilbert_start(cpu_id_p1) .and. current%hilbert_number .le. hilbert_end(cpu_id_p1)) then
         current%last => last
         last%next => current
         last => current
      else
         current%last => null()
         current%next => null()
      endif
      current => current%framework_next
   enddo
   base%last => last
   last%next => base

!----------------------------------------------------------------------------------------------------------------------------------|
!  update blocks
!----------------------------------------------------------------------------------------------------------------------------------|
   n_blocks(cpu_id_p1) = 0

   current => base%framework_next
   do while (current%lv .ne. 0)
      read(unit_save_state) id
      read(unit_save_state) x
      read(unit_save_state) y
      read(unit_save_state) z

      if (any(current%id .ne. id) .or. any(current%x .ne. x) .or. any(current%y .ne. y) .or. any(current%z .ne. z)) then
         write (*, '(a)') ' Load state error.'
         call mpi_abort (mpi_comm_world, 1, error)
      endif

      read(unit_save_state) current%cpu_id
      read(unit_save_state) current%dx
      read(unit_save_state) current%dy
      read(unit_save_state) current%dz
      read(unit_save_state) current%dt
      read(unit_save_state) current%t
      read(unit_save_state) flag

      if (flag) then
         if (.not. current%var_allocate_flag) then
            current%var_allocate_flag = .true.
            allocate(current%ro(nx_block, ny_block, nz_block), current%mx(nx_block, ny_block, nz_block),                           &
                     current%my(nx_block, ny_block, nz_block), current%mz(nx_block, ny_block, nz_block),                           &
                     current%bx(nx_block, ny_block, nz_block), current%by(nx_block, ny_block, nz_block),                           &
                     current%bz(nx_block, ny_block, nz_block), current%en(nx_block, ny_block, nz_block),                           &
                     current%gx(nx_block, ny_block, nz_block), current%gy(nx_block, ny_block, nz_block),                           &
                     current%gz(nx_block, ny_block, nz_block), current%po(nx_block, ny_block, nz_block))
            allocate(current%fxro_bnd(ny_block, nz_block, n_neighs), current%fxmx_bnd(ny_block, nz_block, n_neighs),               &
                     current%fxmy_bnd(ny_block, nz_block, n_neighs), current%fxmz_bnd(ny_block, nz_block, n_neighs),               &
                     current%fxbx_bnd(ny_block, nz_block, n_neighs), current%fxby_bnd(ny_block, nz_block, n_neighs),               &
                     current%fxbz_bnd(ny_block, nz_block, n_neighs), current%fxen_bnd(ny_block, nz_block, n_neighs),               &
                     current%fxpo_bnd(ny_block, nz_block, n_neighs),                                                               &
                     current%fyro_bnd(nx_block, nz_block, n_neighs), current%fymx_bnd(nx_block, nz_block, n_neighs),               &
                     current%fymy_bnd(nx_block, nz_block, n_neighs), current%fymz_bnd(nx_block, nz_block, n_neighs),               &
                     current%fybx_bnd(nx_block, nz_block, n_neighs), current%fyby_bnd(nx_block, nz_block, n_neighs),               &
                     current%fybz_bnd(nx_block, nz_block, n_neighs), current%fyen_bnd(nx_block, nz_block, n_neighs),               &
                     current%fypo_bnd(nx_block, nz_block, n_neighs),                                                               &
                     current%fzro_bnd(nx_block, ny_block, n_neighs), current%fzmx_bnd(nx_block, ny_block, n_neighs),               &
                     current%fzmy_bnd(nx_block, ny_block, n_neighs), current%fzmz_bnd(nx_block, ny_block, n_neighs),               &
                     current%fzbx_bnd(nx_block, ny_block, n_neighs), current%fzby_bnd(nx_block, ny_block, n_neighs),               &
                     current%fzbz_bnd(nx_block, ny_block, n_neighs), current%fzen_bnd(nx_block, ny_block, n_neighs),               &
                     current%fzpo_bnd(nx_block, ny_block, n_neighs))

            if (ini_value_save_flag .ne. 0) then
               allocate(current%ro0(nx_block, ny_block, nz_block), current%mx0(nx_block, ny_block, ny_block),                      &
                        current%my0(nx_block, ny_block, ny_block), current%mz0(nx_block, ny_block, ny_block),                      &
                        current%bx0(nx_block, ny_block, ny_block), current%by0(nx_block, ny_block, ny_block),                      &
                        current%bz0(nx_block, ny_block, ny_block), current%en0(nx_block, ny_block, ny_block))
            endif
         endif

         if (current%cpu_id .eq. cpu_id) then
            n_blocks(cpu_id_p1) = n_blocks(cpu_id_p1) + 1
         endif

         read(unit_save_state) current%ro
         read(unit_save_state) current%mx
         read(unit_save_state) current%my
         read(unit_save_state) current%mz
         read(unit_save_state) current%bx
         read(unit_save_state) current%by
         read(unit_save_state) current%bz
         read(unit_save_state) current%en
         read(unit_save_state) current%gx
         read(unit_save_state) current%gy
         read(unit_save_state) current%gz
         read(unit_save_state) current%po

         if (ini_value_save_flag .ne. 0) then
            read(unit_save_state) current%ro0
            read(unit_save_state) current%mx0
            read(unit_save_state) current%my0
            read(unit_save_state) current%mz0
            read(unit_save_state) current%bx0
            read(unit_save_state) current%by0
            read(unit_save_state) current%bz0
            read(unit_save_state) current%en0
         endif
      else
         if (current%var_allocate_flag) then
            current%var_allocate_flag = .false.
            deallocate(current%ro, current%mx, current%my, current%mz, current%bx, current%by, current%bz, current%en,             &
                       current%gx, current%gy, current%gz, current%po)
            deallocate(current%fxro_bnd, current%fxmx_bnd, current%fxmy_bnd, current%fxmz_bnd,                                     &
                       current%fxbx_bnd, current%fxby_bnd, current%fxbz_bnd, current%fxen_bnd, current%fxpo_bnd,                   &
                       current%fyro_bnd, current%fymx_bnd, current%fymy_bnd, current%fymz_bnd,                                     &
                       current%fybx_bnd, current%fyby_bnd, current%fybz_bnd, current%fyen_bnd, current%fypo_bnd,                   &
                       current%fzro_bnd, current%fzmx_bnd, current%fzmy_bnd, current%fzmz_bnd,                                     &
                       current%fzbx_bnd, current%fzby_bnd, current%fzbz_bnd, current%fzen_bnd, current%fzpo_bnd)
            if (ini_value_save_flag .ne. 0) then
               deallocate(current%ro0, current%mx0, current%my0, current%mz0, current%bx0, current%by0, current%bz0, current%en0)
            endif
         endif
      endif

      current => current%framework_next
   enddo

!if (cpu_id .eq. 1) then
!   current => base%framework_next
!   do while (current%lv .ne. 0)
!      print*, current%cpu_id, current%hilbert_number
!      current => current%framework_next
!   enddo
!pause
!endif

   close (unit_save_state)

!----------------------------------------------------------------------------------------------------------------------------------|
   return
end subroutine load_state
